#!/usr/bin/env python3
"""
自适应页面管理器
根据磁盘空间动态调整页面分配策略
"""

import os
import json
import time
import threading
from typing import Dict, Any, Optional, List, Set
from .disk_monitor import DiskMonitor, StorageLevel
from .storage_adjuster import StorageAdjuster, StorageConfig

class AdaptivePageManager:
    """自适应页面管理器"""
    
    def __init__(self, data_directory: str = "data", page_size: int = 4096):
        """初始化自适应页面管理器"""
        self.data_directory = data_directory
        self.page_size = page_size
        self.page_info_file = os.path.join(data_directory, "page_info.json")
        
        # 磁盘监控和存储调整器
        self.disk_monitor = DiskMonitor(data_directory)
        self.storage_adjuster = StorageAdjuster(data_directory)
        
        # 页面信息
        self.page_info = self._load_page_info()
        
        # 自适应参数
        self.adaptive_params = {
            'compaction_threshold': 0.3,      # 压缩阈值
            'defragmentation_threshold': 0.5,  # 碎片整理阈值
            'preallocation_factor': 1.2,      # 预分配因子
            'cleanup_interval': 300,          # 清理间隔(秒)
            'max_fragmentation': 0.4          # 最大碎片率
        }
        
        # 统计信息
        self.stats = {
            'total_pages': 0,
            'allocated_pages': 0,
            'free_pages': 0,
            'fragmented_pages': 0,
            'compaction_count': 0,
            'defragmentation_count': 0
        }
        
        # 线程锁
        self.lock = threading.RLock()
        
        # 配置存储调整器
        config = StorageConfig(
            page_size=page_size,
            max_pages=self._calculate_max_pages()
        )
        self.storage_adjuster.update_base_config(config)
        
        # 注册回调
        self.storage_adjuster.register_callback('storage_adjusted', self._on_storage_adjusted)
        self.storage_adjuster.register_callback('auto_cleanup', self._on_auto_cleanup)
        
        # 启动监控
        self.storage_adjuster.start_monitoring(interval=30)
        
        # 启动定期清理
        self._start_periodic_cleanup()
    
    def _load_page_info(self) -> Dict[str, Any]:
        """加载页面信息"""
        default_info = {
            'next_page_id': 0,
            'free_pages': [],
            'allocated_pages': {},
            'table_pages': {},
            'fragmented_pages': [],
            'compaction_history': [],
            'defragmentation_history': []
        }
        
        if os.path.exists(self.page_info_file):
            try:
                with open(self.page_info_file, 'r', encoding='utf-8') as f:
                    loaded_info = json.load(f)
                    for key, default_value in default_info.items():
                        if key not in loaded_info:
                            loaded_info[key] = default_value
                        elif key in ['allocated_pages', 'table_pages'] and not isinstance(loaded_info[key], dict):
                            loaded_info[key] = {}
                        elif key in ['free_pages', 'fragmented_pages', 'compaction_history', 'defragmentation_history'] and not isinstance(loaded_info[key], list):
                            loaded_info[key] = []
                    return loaded_info
            except Exception as e:
                print(f"加载页面信息失败: {e}")
        
        return default_info
    
    def _save_page_info(self):
        """保存页面信息"""
        try:
            os.makedirs(self.data_directory, exist_ok=True)
            with open(self.page_info_file, 'w', encoding='utf-8') as f:
                json.dump(self.page_info, f, indent=2, ensure_ascii=False)
        except Exception as e:
            print(f"保存页面信息失败: {e}")
    
    def _calculate_max_pages(self) -> int:
        """计算最大页面数"""
        disk_info = self.disk_monitor.get_disk_info()
        # 使用可用磁盘空间的80%来计算最大页面数
        available_space = disk_info.free * 0.8
        return int(available_space // self.page_size)
    
    def _on_storage_adjusted(self, data: Dict):
        """存储调整回调"""
        new_config = data['new_config']
        disk_info = data['disk_info']
        
        print(f"页面管理策略调整: 存储级别 {disk_info.level.value}")
        
        # 根据存储级别调整策略
        if disk_info.level in [StorageLevel.CRITICAL, StorageLevel.LOW]:
            # 空间不足，执行压缩和碎片整理
            self._perform_compaction()
            self._perform_defragmentation()
        elif disk_info.level == StorageLevel.EXCESS:
            # 空间充足，可以预分配页面
            self._perform_preallocation()
    
    def _on_auto_cleanup(self, data: Dict):
        """自动清理回调"""
        print("执行页面管理自动清理...")
        self._cleanup_unused_pages()
        self._optimize_page_layout()
    
    def _start_periodic_cleanup(self):
        """启动定期清理"""
        def cleanup_loop():
            while True:
                time.sleep(self.adaptive_params['cleanup_interval'])
                try:
                    self._periodic_cleanup()
                except Exception as e:
                    print(f"定期清理错误: {e}")
        
        cleanup_thread = threading.Thread(target=cleanup_loop, daemon=True)
        cleanup_thread.start()
    
    def _periodic_cleanup(self):
        """定期清理"""
        with self.lock:
            # 检查碎片率
            fragmentation_rate = self._calculate_fragmentation_rate()
            
            if fragmentation_rate > self.adaptive_params['max_fragmentation']:
                print(f"碎片率过高 ({fragmentation_rate:.2%})，执行碎片整理")
                self._perform_defragmentation()
            
            # 清理未使用的页面
            self._cleanup_unused_pages()
    
    def _calculate_fragmentation_rate(self) -> float:
        """计算碎片率"""
        total_pages = len(self.page_info['allocated_pages']) + len(self.page_info['free_pages'])
        if total_pages == 0:
            return 0.0
        
        fragmented_pages = len(self.page_info['fragmented_pages'])
        return fragmented_pages / total_pages
    
    def _perform_compaction(self):
        """执行页面压缩"""
        with self.lock:
            print("执行页面压缩...")
            
            # 压缩逻辑：合并相邻的空闲页面
            free_pages = sorted(self.page_info['free_pages'])
            compacted_pages = []
            
            i = 0
            while i < len(free_pages):
                start_page = free_pages[i]
                end_page = start_page
                
                # 找到连续的空闲页面
                while i + 1 < len(free_pages) and free_pages[i + 1] == end_page + 1:
                    i += 1
                    end_page = free_pages[i]
                
                compacted_pages.append((start_page, end_page))
                i += 1
            
            # 更新空闲页面列表
            self.page_info['free_pages'] = compacted_pages
            self.stats['compaction_count'] += 1
            
            # 记录压缩历史
            self.page_info['compaction_history'].append({
                'timestamp': time.time(),
                'compacted_ranges': len(compacted_pages),
                'total_pages': sum(end - start + 1 for start, end in compacted_pages)
            })
            
            # 保持历史记录不超过100条
            if len(self.page_info['compaction_history']) > 100:
                self.page_info['compaction_history'].pop(0)
            
            self._save_page_info()
            print(f"页面压缩完成，压缩了 {len(compacted_pages)} 个页面范围")
    
    def _perform_defragmentation(self):
        """执行碎片整理"""
        with self.lock:
            print("执行碎片整理...")
            
            # 碎片整理逻辑：重新排列页面以减少碎片
            allocated_pages = self.page_info['allocated_pages']
            table_pages = self.page_info['table_pages']
            
            # 按表分组页面
            table_page_groups = {}
            for page_id, page_info in allocated_pages.items():
                table_name = page_info.get('table_name', 'unknown')
                if table_name not in table_page_groups:
                    table_page_groups[table_name] = []
                table_page_groups[table_name].append(page_id)
            
            # 为每个表重新分配连续的页面
            defragmented_count = 0
            for table_name, page_ids in table_page_groups.items():
                if len(page_ids) > 1:  # 只有多个页面才需要整理
                    # 分配新的连续页面
                    new_page_ids = self._allocate_consecutive_pages(len(page_ids))
                    if new_page_ids:
                        # 更新页面映射
                        for old_id, new_id in zip(page_ids, new_page_ids):
                            if old_id in allocated_pages:
                                allocated_pages[new_id] = allocated_pages[old_id]
                                del allocated_pages[old_id]
                        
                        # 更新表页面映射
                        if table_name in table_pages:
                            table_pages[table_name] = new_page_ids
                        
                        defragmented_count += len(page_ids)
            
            self.stats['defragmentation_count'] += 1
            
            # 记录碎片整理历史
            self.page_info['defragmentation_history'].append({
                'timestamp': time.time(),
                'defragmented_pages': defragmented_count,
                'affected_tables': len(table_page_groups)
            })
            
            # 保持历史记录不超过100条
            if len(self.page_info['defragmentation_history']) > 100:
                self.page_info['defragmentation_history'].pop(0)
            
            self._save_page_info()
            print(f"碎片整理完成，整理了 {defragmented_count} 个页面")
    
    def _perform_preallocation(self):
        """执行页面预分配"""
        with self.lock:
            print("执行页面预分配...")
            
            # 预分配一些页面以提高性能
            preallocation_count = int(len(self.page_info['free_pages']) * 
                                    self.adaptive_params['preallocation_factor'])
            
            if preallocation_count > 0:
                # 预分配页面
                preallocated_pages = []
                for i in range(preallocation_count):
                    page_id = self._get_next_page_id()
                    preallocated_pages.append(page_id)
                
                # 添加到空闲页面列表
                self.page_info['free_pages'].extend(preallocated_pages)
                self._save_page_info()
                
                print(f"预分配了 {preallocation_count} 个页面")
    
    def _cleanup_unused_pages(self):
        """清理未使用的页面"""
        with self.lock:
            print("清理未使用的页面...")
            
            # 清理长时间未使用的页面
            current_time = time.time()
            cleanup_threshold = 3600  # 1小时
            
            pages_to_remove = []
            for page_id, page_info in self.page_info['allocated_pages'].items():
                last_access = page_info.get('last_access', 0)
                if current_time - last_access > cleanup_threshold:
                    pages_to_remove.append(page_id)
            
            # 移除未使用的页面
            for page_id in pages_to_remove:
                self._free_page(page_id)
            
            print(f"清理了 {len(pages_to_remove)} 个未使用的页面")
    
    def _optimize_page_layout(self):
        """优化页面布局"""
        with self.lock:
            print("优化页面布局...")
            
            # 优化逻辑：重新排列页面以提高访问效率
            table_pages = self.page_info['table_pages']
            
            for table_name, page_ids in table_pages.items():
                if len(page_ids) > 1:
                    # 按访问频率排序页面
                    page_access_info = []
                    for page_id in page_ids:
                        if page_id in self.page_info['allocated_pages']:
                            access_count = self.page_info['allocated_pages'][page_id].get('access_count', 0)
                            page_access_info.append((page_id, access_count))
                    
                    # 按访问频率排序
                    page_access_info.sort(key=lambda x: x[1], reverse=True)
                    
                    # 重新分配页面，将访问频率高的页面放在前面
                    new_page_ids = [page_id for page_id, _ in page_access_info]
                    table_pages[table_name] = new_page_ids
            
            self._save_page_info()
            print("页面布局优化完成")
    
    def _allocate_consecutive_pages(self, count: int) -> List[int]:
        """分配连续的页面"""
        # 查找连续的空闲页面
        free_pages = sorted(self.page_info['free_pages'])
        
        for i in range(len(free_pages) - count + 1):
            # 检查是否连续
            consecutive = True
            for j in range(1, count):
                if free_pages[i + j] != free_pages[i] + j:
                    consecutive = False
                    break
            
            if consecutive:
                # 分配连续页面
                allocated_pages = free_pages[i:i + count]
                # 从空闲列表中移除
                self.page_info['free_pages'] = (
                    free_pages[:i] + free_pages[i + count:]
                )
                return allocated_pages
        
        return []
    
    def _get_next_page_id(self) -> int:
        """获取下一个页面ID"""
        page_id = self.page_info['next_page_id']
        self.page_info['next_page_id'] += 1
        return page_id
    
    def _free_page(self, page_id: int):
        """释放页面"""
        if page_id in self.page_info['allocated_pages']:
            del self.page_info['allocated_pages'][page_id]
            self.page_info['free_pages'].append(page_id)
    
    def allocate_page(self, table_name: str) -> int:
        """分配页面"""
        with self.lock:
            # 优先使用空闲页面
            if self.page_info['free_pages']:
                page_id = self.page_info['free_pages'].pop(0)
            else:
                page_id = self._get_next_page_id()
            
            # 记录页面信息
            self.page_info['allocated_pages'][page_id] = {
                'table_name': table_name,
                'allocated_time': time.time(),
                'last_access': time.time(),
                'access_count': 0
            }
            
            # 更新表页面映射
            if table_name not in self.page_info['table_pages']:
                self.page_info['table_pages'][table_name] = []
            self.page_info['table_pages'][table_name].append(page_id)
            
            self._save_page_info()
            return page_id
    
    def deallocate_page(self, page_id: int):
        """释放页面"""
        with self.lock:
            if page_id in self.page_info['allocated_pages']:
                page_info = self.page_info['allocated_pages'][page_id]
                table_name = page_info['table_name']
                
                # 从表页面映射中移除
                if table_name in self.page_info['table_pages']:
                    if page_id in self.page_info['table_pages'][table_name]:
                        self.page_info['table_pages'][table_name].remove(page_id)
                
                # 释放页面
                self._free_page(page_id)
                self._save_page_info()
    
    def get_table_pages(self, table_name: str) -> List[int]:
        """获取表的页面列表"""
        with self.lock:
            return self.page_info['table_pages'].get(table_name, [])
    
    def update_page_access(self, page_id: int):
        """更新页面访问信息"""
        with self.lock:
            if page_id in self.page_info['allocated_pages']:
                page_info = self.page_info['allocated_pages'][page_id]
                page_info['last_access'] = time.time()
                page_info['access_count'] = page_info.get('access_count', 0) + 1
    
    def get_stats(self) -> Dict[str, Any]:
        """获取统计信息"""
        with self.lock:
            total_pages = len(self.page_info['allocated_pages']) + len(self.page_info['free_pages'])
            fragmentation_rate = self._calculate_fragmentation_rate()
            
            return {
                'total_pages': total_pages,
                'allocated_pages': len(self.page_info['allocated_pages']),
                'free_pages': len(self.page_info['free_pages']),
                'fragmented_pages': len(self.page_info['fragmented_pages']),
                'fragmentation_rate': fragmentation_rate,
                'compaction_count': self.stats['compaction_count'],
                'defragmentation_count': self.stats['defragmentation_count'],
                'next_page_id': self.page_info['next_page_id']
            }
    
    def get_storage_status(self) -> Dict[str, Any]:
        """获取存储状态"""
        return self.storage_adjuster.get_storage_status()
    
    def print_status(self):
        """打印状态"""
        stats = self.get_stats()
        storage_status = self.get_storage_status()
        
        print("=" * 60)
        print("自适应页面管理状态")
        print("=" * 60)
        print(f"总页面数: {stats['total_pages']}")
        print(f"已分配页面: {stats['allocated_pages']}")
        print(f"空闲页面: {stats['free_pages']}")
        print(f"碎片页面: {stats['fragmented_pages']}")
        print(f"碎片率: {stats['fragmentation_rate']:.2%}")
        print(f"压缩次数: {stats['compaction_count']}")
        print(f"碎片整理次数: {stats['defragmentation_count']}")
        print(f"下一个页面ID: {stats['next_page_id']}")
        print()
        
        # 存储状态
        disk_info = storage_status['disk_info']
        print("存储状态:")
        print(f"  磁盘使用率: {disk_info['percentage']:.1f}%")
        print(f"  存储级别: {disk_info['level']}")
        print(f"  可用空间: {self.disk_monitor.format_size(disk_info['free'])}")
        print()
        
        # 表页面分布
        table_pages = self.page_info['table_pages']
        if table_pages:
            print("表页面分布:")
            for table_name, page_ids in table_pages.items():
                print(f"  {table_name}: {len(page_ids)} 个页面")
    
    def force_cleanup(self):
        """强制清理"""
        with self.lock:
            self._cleanup_unused_pages()
            self._perform_compaction()
            self._perform_defragmentation()
            return self.storage_adjuster.force_cleanup()
    
    def shutdown(self):
        """关闭页面管理器"""
        print("关闭自适应页面管理器...")
        
        # 停止监控
        self.storage_adjuster.stop_monitoring()
        
        # 保存页面信息
        self._save_page_info()
        
        print("自适应页面管理器已关闭")